#include <iostream>
#include <vector>
#include <complex>

#include "ber.h"
#include "spec.h"
#include "decision.h"
#include "search.h"
#include "energy.h"
#include "synth.h"
#include "pareto.h"
#include <fstream>
#include <iterator>
#include "weighted.h"
#include "peakfactors.h"
#include "smoothness.h"
#include "distances.h"
#include "suppress.h"

template < typename SignalSystem>
void save(const SignalSystem& sys)
{
    ofstream file("out.csv");
    
    for (int i = 0; i < sys.size(); ++i)
    {
        for(int j = 0; j < sys[i].size(); ++ j)
        {
           file << sys[i][j].real();
           if(j != sys[i].size() - 1) file << ", ";
        }
        
        
        file << endl;
        
        for(int j = 0; j < sys[i].size(); ++ j)
        {
           file << sys[i][j].imag();
           if(j != sys[i].size() - 1) file << ", ";
        }  
        file << endl;
    }
    
    cout << endl;
}

int main()
{
    using namespace std;
    
    typedef vector<complex<double> > Signal;
    typedef vector<Signal> SignalSystem;
    
    const unsigned volume = 16;
    const unsigned length = 28;
    SignalSystem sys = SignalSystem(1, Signal(length, 0));

    /*for(auto it = sys.begin(); it != sys.end(); ++ it)
    {
        generate(it->begin(), it->end(), randomValue<Double<5, -1>>);
    }*/
    typedef Double<1, 0> SignalToNoiseRate;
    typedef Double<1, -1> Compression;
    
    for (int j = 0; j < volume - 2; ++ j)
    {
        sys.push_back(Signal(length, 0));
        cout << "*** volume *** " << sys.size() << endl; 
        generate(sys.back().begin(), sys.back().end(), randomValue<Double<5, -1>>);
    
        
        Synth
        <
            SignalSystem,
            RandomSearchX,
            Decision
            <
                Weighted,
                //Weight<BitErrorRate<SignalToNoiseRate, 100>, Double<1, 0>>,
                Weight<OutBandEmission<Compression>, Double<100, 0>>,
                Weight<Smoothness, Double<1, 0>>,
                Weight<MinDistance, Double<1, 0>>,
                Weight<AvrDistance, Double<1, 0>>,
                Weight<SpecPeakN<Compression>, Double<100, 0>>
            >
        > synth(sys);
        
        sys = synth();
        
        BitErrorRate<SignalToNoiseRate, 10000> bitrate;
        OutBandEmission<Compression> emission;
        Energy energy;
        PeakFactor peak;
        Smoothness smoothness;
        MinDistance minDistance;
        AvrDistance avrDistance;
        SpecPeakN<Compression> specPeakN;
        cout << " * * * RESULTS * * * " << endl
             << bitrate.getName() << ": " << bitrate(sys) << endl
             << emission.getName() << ": " << emission(sys) << endl
             << energy.getName() << ": " << energy(sys) << endl
             << peak.getName() << ": " << peak(sys) << endl
             << smoothness.getName() << ": " << smoothness(sys) << endl
             << minDistance.getName() << ": " << minDistance(sys) << endl
             << avrDistance.getName() << ": " << avrDistance(sys) << endl
             << specPeakN.getName() << ": " << specPeakN(sys) << endl;
    };
        
        Suppressor<Compression> supp;
        
        sys = supp(sys);
              
        
        BitErrorRate<SignalToNoiseRate, 10000> bitrate;
        OutBandEmission<Compression> emission;
        Energy energy;
        PeakFactor peak;
        Smoothness smoothness;
        MinDistance minDistance;
        AvrDistance avrDistance;
        SpecPeakN<Compression> specPeakN;
        cout << " * * * RESULTS * * * " << endl
             << bitrate.getName() << ": " << bitrate(sys) << endl
             << emission.getName() << ": " << emission(sys) << endl
             << energy.getName() << ": " << energy(sys) << endl
             << peak.getName() << ": " << peak(sys) << endl
             << smoothness.getName() << ": " << smoothness(sys) << endl
             << minDistance.getName() << ": " << minDistance(sys) << endl
             << avrDistance.getName() << ": " << avrDistance(sys) << endl
             << specPeakN.getName() << ": " << specPeakN(sys) << endl;
        
    save(sys);        
}
